State!

We've learned how to display data, and pass data as props. What we haven't learned is how to manipulate data! This is where state comes in.

Suppose we wanted to start counting upward when clicking the child's button. There are two ways that you'll see as an approach to solving this problem:

 


 

State: Classes

We're not going to spend too much time on classes, but since they're not deprecated, we're definitely going to talk about them for a brief moment. For reference to what's happening, the following code is from ParentClassComponent.jsx in the example-state project:

 

Each Component's State is Independent!

Regardless of whether or not you use multiple of the same component, they will each have their own state. Try replacing <ParentComponent /> in the ReactDOM.render with <ParentChildParty /> in the index.js file.

Each ParentComponent has its own state that is accessed individually!

 

 


 

State's Asynchronicity:

Asynchronicity, not a word, but it sounds cool, and setState is an asynchronous function. Let's try adding a console log to read from state immediately after we set it:

What ends up happening is that we wind up getting a console log in our browser that's logging the previous state, while our browser itself is rendering what the actual state is. For example, suppose this.state.timesPressed was 2, if we called handAction, we'll then asynchronously call setState, and then move onto console.log, where 2 will get printed to the console while state is being updated to 3!

 

Keeping your code clean with setState:

To keep your code clean, you can just as easily pass a function to your set state that returns a state object of whatever you wish to update. The function by default takes in (state, props). That is, it takes in the previous state and props (prior to being updated). You can see an example of this in ParentClassWithStateFunction.jsx in the corresponding code.

 

More State Functions

You can just as easily create other methods that work on the same state variables, that is, it's somewhat pointless having only a single function that works on a state variable. You'll often want to have multiple functions referencing the same state variables to tie your app together, such as a reset function for our button counter (an example can be seen in the ParentClassWithResetFunction.jsx file:

 

REITERATING: In order to make sure that you don't find yourself in a pickle (i.e. if you use a class component with constructors), make sure you always bind your functions! (Try removing a this.<functionName> = this.<functionName>.bind(this) from the constructor)

 

Multiple State Elements:

What happens if you have more than one element in your state? You likely will want more than one element in your state, such as a text field (example can be seen in ParentClassMultipleStateItems.jsx):

 

How to Update State (and not)

When you have more and more complex states, you'll often have compartmentalized data in the form of an object, or an array, etc (may as a user object with a username and password?). When updating your state, I'm sure you've noticed that you only insert the portion of state that you'd like to update. This works great at the top level. That's because state is updated with a shallow copy. If you try to do this with objects in state, it will not deep copy. Take a look at the below code (working components in ParentComponentShallowDeepState.jsx). When we update username or password, we're not passing in its corresponding value (that is, on username, we're not also passing in password, and vice versa).

 

Try running the below code. It doesn't necessarily make the most sense for the text boxes (since they don't rerender the values), so we have the username and password displaying within a div after each:

 

I'm sure you noticed that the second you started typing in password, the username went away (and the same goes for username). To avoid this problem, you only need to either pass in the part of state you wish to keep:

 

However, it's pretty likely that you'll have more than just two keys within your object, so you can use the spread operator to get around that and spread whatever object you'd like (in our case, it's this.state.user):

 

 

What's happening above is that we're essentially copying this.state.user and then overwriting a specific key. CAVEAT: Make sure you spread your object first. If you spread it last, then you'll be overwriting your new data with your old data! Go to ParentComponentShallowDeepState.jsx to see a working example. Try placing the the spread after your updated values. What do you see?

 

Cleaning Up:

We've seen that we can update and manipulate our state with the setState methods, but our components are getting incredibly large. Keeping file sizes below a hundred lines is definitely nice to do. Let's clean our code up a little bit. We can start with making use of arrow functions! Because arrow functions don't bind to their own this and move up a level, they implicitly bind to the class itself (so we don't have to remember all of those this.whatever = this.whatever.bind(this)).

So now we can remove the constructor entirely, set our state by just creating a state variable and turning our functions into arrow functions (seen in ParentClassClean.jsx)!